Fix phpstan/phpstan#11488: Wrongly assumed undefined variable since 1.11.10#5149
Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Closed
Fix phpstan/phpstan#11488: Wrongly assumed undefined variable since 1.11.10#5149phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
- Fixed specifyTypesForCountFuncCall falsey context to build complement types directly instead of removing matching types, avoiding structural subtyping issues with TypeCombinator::remove - Added regression test in tests/PHPStan/Analyser/nsrt/bug-11488.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes false positive "Variable $operator on left side of ?? is never defined" when using
count($row) !== 1to narrow a union of constant array types likearray{mixed}|array{mixed, string|null, mixed}. After the count check, PHPStan incorrectly narrowed the type to*NEVER*instead ofarray{mixed, string|null, mixed}.Changes
specifyTypesForCountFuncCallinsrc/Analyser/TypeSpecifier.phpto handle the falsey context differently: instead of collecting matching types and removing them viacreate(..., falsey), it now collects non-matching types directly and usesTypeSpecifierContext::createTrue()to set themtests/PHPStan/Analyser/nsrt/bug-11488.phpRoot cause
In the falsey context (e.g.,
count($arr) !== 1),specifyTypesForCountFuncCallbuilt the matching types (those with the target count) and usedcreate(..., falsey)to remove them from the variable's type. This ultimately calledTypeCombinator::remove(), which checks$typeToRemove->isSuperTypeOf($fromType). Sincearray{mixed}is structurally a supertype ofarray{mixed, string|null, mixed}(it only checks that required keys exist with compatible values, not that there are no extra keys), removingarray{mixed}from the union also removedarray{mixed, string|null, mixed}, resulting in*NEVER*.The fix changes the falsey context to build the complement directly: it keeps array types whose count definitely doesn't match the target and skips those that definitely match, then uses truthy context to set the result.
Test
Added
tests/PHPStan/Analyser/nsrt/bug-11488.phpwhich verifies that aftercount($row) !== 1where$rowisarray{mixed}|array{mixed, string|null, mixed}, the type is correctly narrowed toarray{mixed, string|null, mixed}and destructured variables like$operatorhave the expectedstring|nulltype.Fixes phpstan/phpstan#11488